package com.nulldev.util.web.nTubeExtensions.Redwood;

import java.util.regex.Pattern;

import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.io.FastIO.FastIO;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.web.HttpClient.HttpClient;
import com.nulldev.util.web.YouTubeExtractor.signatures.SignatureExtractor;

public class RedwoodAutoUpdater {

	/*
	 * Redwood: Dynamic Player Patcher (DPP) In order for Redwood to work correctly
	 * on nTube, the following patches must be applied.
	 * 
	 * func_stream_validator Removes the Google-only url checker which throws an
	 * untrusted URL error on non-google URLs. (Required) ref_doubleclick Removes a
	 * connection to Google's ad servers. (Optional) image_tracking Removes
	 * image-based tracking. (Optional) no_v9_decipher Disables YouTube's v9
	 * decipherer. (Optional)
	 */

	private static final Logger log = LoggerFactory.getLogger();
	private static final boolean enableLegacyCodePaths = false;

	private static final String LEGACY_FUNC_STREAM_VALIDATOR = "function(a,b){return b&&b.test(a)?!0:!1};";
	private static final String LEGACY_FUNC_STREAM_REPLACED_VALUE = "function(a,b){return RedwoodPreprocessor.isVideoURLAllowed(a, b);};";

	private static final String FUNC_STREAM_VALIDATOR = "{return b&&b.test(a)";
//	private static final String FUNC_STREAM_VALIDATOR_END = "?!0:!1};";
	private static final String FUNC_STREAM_REPLACED_VALUE = "{return RedwoodPreprocessor.isVideoURLAllowed2(arguments);";

	private static final String STRING_REF_DOUBLECLICK = "(\"//static.doubleclick.net/instream/ad_status.js\",";
	private static final String STRING_REF_REPLACED_VALUE = "(\"//static.fuckadvertising.local/instream/ad_status.js\",";

	private static final String FUNC_IMAGE_TRACKING = "var c=document.createElement(\"img\");";
	private static final String FUNC_IMAGE_TRACKING_END = ")};\nreturn c";
	private static final String FUNC_IMAGE_REPLACED_VALUE = "console.debug(\"[fuckAdvertising::ImageTracking] blocked: \", arguments);var c = undefined;";

	private static final String FUNC_V9_DEC = "=function(a){var b=a.split(\"\"),c=[";
	private static final String FUNC_V9_DEC_END = "a}return b.join(\"\")};";
	private static final String FUNC_V9_DEC_REPLACED_VALUE = "=function(a){return a;};";

	public static String process(String player) {
		if (enableLegacyCodePaths) {
			final boolean hasStreamValidator = player.contains(LEGACY_FUNC_STREAM_VALIDATOR);
			if (!hasStreamValidator)
				throw new UnsupportedOperationException("process: Critical component STREAM_VALIDATOR was NOT patched!");
			else {
				player = player.replace(LEGACY_FUNC_STREAM_VALIDATOR, LEGACY_FUNC_STREAM_REPLACED_VALUE);
			}
		} else {
			final int loc = player.indexOf(FUNC_STREAM_VALIDATOR);
			if (loc < 0)
				throw new UnsupportedOperationException("process: Critical component STREAM_VALIDATOR was NOT patched! [err=INDEX_NOT_FOUND]");
			final String c = player.substring(loc - 1);
			final String g = StringsUtil.substringBetweenO(c, FUNC_STREAM_VALIDATOR, "};");
			if (g == null || g.isEmpty())
				throw new UnsupportedOperationException("process: Critical component STREAM_VALIDATOR was NOT patched! [err=SUBSTRING_FAIL]");
			player = player.replace(FUNC_STREAM_VALIDATOR + g, FUNC_STREAM_REPLACED_VALUE);
		}
		final boolean hasDoubleClickRef = player.contains(STRING_REF_DOUBLECLICK);
		if (!hasDoubleClickRef)
			log.warn("process: Failed to patch 'ref_doubleclick'!");
		else {
			player = player.replace(STRING_REF_DOUBLECLICK, STRING_REF_REPLACED_VALUE);
		}
		final boolean hasImageTrackingRef = player.contains(FUNC_IMAGE_TRACKING) && player.contains(FUNC_IMAGE_TRACKING_END);
		if (!hasImageTrackingRef)
			log.warn("process: Failed to patch 'image_tracking'!");
		else {
			final int loc = player.indexOf(FUNC_IMAGE_TRACKING);
			if (loc == -1) {
				log.warn("process: Failed to find pattern for 'image_tracking'!");
				return player;
			}
			final String c = player.substring(loc - 1);
			final String g = StringsUtil.substringBetweenO(c, "{" + FUNC_IMAGE_TRACKING, "};");
			if (g == null || g.isEmpty()) {
				log.warn("process: Failed to find function for 'image_tracking'!");
				return player;
			}
			player = player.replace("{" + FUNC_IMAGE_TRACKING + g + "};", "{" + FUNC_IMAGE_REPLACED_VALUE + FUNC_IMAGE_TRACKING_END);
			player = player.replaceAll(Pattern.quote(FUNC_IMAGE_TRACKING_END), "");
		}
		final boolean hasV9Cipher = player.contains(FUNC_V9_DEC) && player.contains(FUNC_V9_DEC_END);
		if (!hasV9Cipher)
			log.warn("process: Failed to patch 'v9_decipherer'!");
		else {
			final int loc = player.indexOf(FUNC_V9_DEC);
			if (loc == -1) {
				log.warn("process: Failed to find pattern for 'v9_decipherer'!");
				return player;
			}
			final String c = player.substring(loc - 1);
			final String g = StringsUtil.substringBetweenO(c, FUNC_V9_DEC, FUNC_V9_DEC_END);
			if (g == null || g.isEmpty()) {
				log.warn("process: Failed to find function for 'v9_decipherer'!");
				return player;
			}
			player = player.replace(FUNC_V9_DEC + g + FUNC_V9_DEC_END, FUNC_V9_DEC_REPLACED_VALUE);
		}
		return player;
	}

	public static void main(String[] args) throws Exception {
		final String c = HttpClient.getString().url("https://www.youtube.com/").create().dataThenClose();
		final String playerURL = SignatureExtractor.getPlayerURL(c);
		log.info("playerURL: " + playerURL);
		final String p = HttpClient.getString().url(playerURL).create().dataThenClose();
		final String r = process(p);
		log.info(r);
		FastIO.get().flush();
	}
}
